/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package session.TX;

import Helper.TTAHelper.Helper_Record_TestingOnly;
import entity.Outlet;
import entity.OutletReservation;
import entity.PurchasedItem_Regular;
import entity.Users;
import entity.tta.OutletDataModel;
import entity.tta.StakeholderDataModel;
import entity.tta.StrDblRecord;
import entity.tta.StrLongRecord;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 *
 * @author Chen Zhe
 */
@Stateless
public class StakeholderDataModelSB implements StakeholderDataModelSBRemote {

    @PersistenceContext
    EntityManager em;

    public StakeholderDataModelSB() {
    }

    @Override
    public void updateDataModel(String stakeholderName) {

        Calendar fromTime = Calendar.getInstance();
        String[] countries;
        String[] ageGroups;

        Query q1 = em.createQuery("SELECT c FROM StakeholderDataModel c WHERE c.stakeholderName = :name");
        q1.setParameter("name", stakeholderName);
        List<StakeholderDataModel> stakeholderModels = q1.getResultList();
        StakeholderDataModel stakeholderModel;
        if (stakeholderModels.isEmpty()) {
            stakeholderModel = new StakeholderDataModel();
        } else {
            stakeholderModel = stakeholderModels.get(0);
        }

        fromTime = stakeholderModel.getTime_stamp();
        countries = stakeholderModel.getCountriesArray();
        ageGroups = stakeholderModel.getAgeGroupsArray();
        List<StrDblRecord> strDblRecords = stakeholderModel.getStrDblRecords();
        List<StrLongRecord> strLongRecords = stakeholderModel.getStrLongRecords();

//------update cluster---------------------------------------------------------------------------------------------------------
        Long[][] cluster = stakeholderModel.getCluster();
        cluster = updateCluster(cluster, stakeholderName, fromTime, countries, ageGroups);
        stakeholderModel.setCluster(cluster);
        em.persist(stakeholderModel);
        em.flush();

//------update cluster which is updated within 2 years---------------------------------------------------------------------------------------------------------
        Long[][] clusterW2Y = updateClusterW2Y(stakeholderName, countries, ageGroups);
        stakeholderModel.setClusterW2Y(clusterW2Y);
        em.persist(stakeholderModel);
        em.flush();
        
        
        int[] intervals = new int[]{6, 12, 18, 24, 48, 96};
        for (int i = 0; i < intervals.length; i++) {
//------updateSaleAgeGroup(outletId, fromTime, countries, ageGroups) used in DMS page------------------------------------ 
            strDblRecords = stakeholderModel.getStrDblRecords();
            strDblRecords = updateSaleAgeGroupWithin(strDblRecords, stakeholderName, intervals[i], ageGroups);
            stakeholderModel.setStrDblRecords(strDblRecords);
            em.persist(stakeholderModel);
            em.flush();
            String type = "S_"+stakeholderName+"SaleAgeGroupW" + intervals[i] + "M";
            deleteOldDblRecords(stakeholderModel.getStrDblRecords(), type);

//------updateSaleCountry(outletId, fromTime, countries, ageGroups) used in DMS page--------------------------------------  
            strDblRecords = stakeholderModel.getStrDblRecords();
            strDblRecords = updateSaleCountryWithin(strDblRecords, stakeholderName, intervals[i]);
            stakeholderModel.setStrDblRecords(strDblRecords);
            em.persist(stakeholderModel);
            em.flush();
            type = "S_"+stakeholderName+"SaleCountryW" + intervals[i] + "M";
            deleteOldDblRecords(stakeholderModel.getStrDblRecords(), type);

//------updateReservationAgeGroup(outletId, fromTime, countries, ageGroups) used in DMS page------------------------------
            strLongRecords = stakeholderModel.getStrLongRecords();
            strLongRecords = updateReservationAgeGroupWithin(strLongRecords, stakeholderName, intervals[i], ageGroups);
            stakeholderModel.setStrLongRecords(strLongRecords);
            em.persist(stakeholderModel);
            em.flush();
            type = "S_"+stakeholderName+"ReservationAgeGroupW" + intervals[i] + "M";
            deleteOldLongRecords(stakeholderModel.getStrLongRecords(), type);

//
////------updateReservationCountry(outletId, fromTime, countries, ageGroups) used in DMS page------------------------------
            strLongRecords = stakeholderModel.getStrLongRecords();
            strLongRecords = updateReservationCountryWithin(strLongRecords, stakeholderName, intervals[i]);
            stakeholderModel.setStrLongRecords(strLongRecords);
            em.persist(stakeholderModel);
            em.flush();
            type = "S_"+stakeholderName+"ReservationCountryW" + intervals[i] + "M";
            deleteOldLongRecords(stakeholderModel.getStrLongRecords(), type);
        }
//
//
//////------updateMonthSale(outletId, fromTime, countries, ageGroups) used in PIP page----------------------------------------------
        strDblRecords = stakeholderModel.getStrDblRecords();
        strDblRecords = updateMonthSale(strDblRecords, stakeholderName, fromTime);
        em.persist(stakeholderModel);
        em.flush();
//
////------updateMonthReservation(outletId, fromTime, countries, ageGroups) used in PIP page---------------------------------------  
        strLongRecords = stakeholderModel.getStrLongRecords();
        strLongRecords = updateMonthReservation(strLongRecords, stakeholderName, fromTime);
        em.persist(stakeholderModel);
        em.flush();

//------update the time_stamp at dataModel to current system time---------------------------------------------------------------
        Calendar cTemp = Calendar.getInstance();
        stakeholderModel.setTime_stamp(cTemp);
        stakeholderModel.setStakeholderName(stakeholderName);
        em.persist(stakeholderModel);
        em.flush();


//        Query q2 = em.createQuery("SELECT c FROM StakeholderFood c WHERE c.name = :name");
//        q2.setParameter("name", stakeholderName);
//        List<StakeholderFood> stakeholders = q2.getRFesultList();
//        StakeholderFood stakeholder = stakeholders.get(0);
//        stakeholder.setDataModel(stakeholderModel);
//        em.persist(stakeholder);

    }

    // update cluster to the current system time  updateCluster: STEP1
    private Long[][] updateCluster(Long[][] cluster, String stakeholderName, Calendar fromTime, String[] countries, String[] ageGroups) {

        //go through the List<PurchasedItem> from fromTime
        List<PurchasedItem_Regular> transactionDatas = getOutletTransactions(stakeholderName, fromTime);
        for (Object o : transactionDatas) {
            PurchasedItem_Regular p = (PurchasedItem_Regular) o;
            //get the country and agrGroup of this PurchasedItem
            String country = p.getBuyer_country();
            Integer age = p.getBuyer_age();
            String ageGroup = getAgeGroup(ageGroups, age);

            //Add one to the corrsponding 2dArray(cluster)  
            int x = getIndex(countries, country);
            int y = getIndex(ageGroups, ageGroup);
            cluster[x][y]++;
        }

        //go through the List<OutletReservation> from fromTime
        List<OutletReservation> outletReservations = getOutletReservations(stakeholderName, fromTime);
        for (Object o : outletReservations) {
            // get customer's country and ageGroup
            OutletReservation r = (OutletReservation) o;
            Users customer = r.getCustomer();
            String customerEmail = customer.getEmail();
            String country = customer.getCountry();
            Calendar birthDay = Calendar.getInstance();
            birthDay.setTime(customer.getBirthDate());
            Calendar currentTime = Calendar.getInstance();
            Integer age = new Integer(currentTime.get(Calendar.YEAR) - birthDay.get(Calendar.YEAR));
            String ageGroup = getAgeGroup(ageGroups, age);
            //add one to the corrsponding 2dArray(cluster)
            int x = getIndex(countries, country);
            int y = getIndex(ageGroups, ageGroup);
            cluster[x][y]++;
        }


        return cluster;
    }

    // get the times of transactions take place at an outlet updateCluster: STEP2
    private List<PurchasedItem_Regular> getOutletTransactions(String stakeholderName, Calendar fromTime) {
        Query q1 = em.createQuery("SELECT c FROM PurchasedItem_Regular c WHERE c.seller = :name AND "
                + "c.time_stamp>:fromTime ORDER BY c.time_stamp");
        q1.setParameter("name", stakeholderName);
        q1.setParameter("fromTime", fromTime);
        List<PurchasedItem_Regular> transactionDatas = getTransactionData(q1.getResultList());
        return transactionDatas;
    }
//        Query query = em.createQuery("SELECT o FROM OutletReservation o WHERE o.outlet.id = :oId"
//                + " AND o.timeslot >= :fromDate AND o.timeslot < :toDate");

//                    Query q1 = em.createQuery("SELECT c FROM OutletReservation c WHERE c.outlet.stakeholder.name = :name AND "
//                + "c.customerShowUp=:customerShowUp AND c.timeslot>=:startTime AND c.timeslot<:untilTime");
    //go through all purchasedItems of the month between startOfMonth and endOfMonth
    //get the representative purchsed items(Transact Data), one transaction will be represented by a single purchased item  updateCluster:step3
    private List<PurchasedItem_Regular> getTransactionData(List<PurchasedItem_Regular> purchasedItems) {
        List<PurchasedItem_Regular> transactionDatas = purchasedItems;
        Calendar tempTimeStamp = Calendar.getInstance();
        //Have to initialize the tempTimeStamp, When run it the first time, set year to 1000 will get all the purchased item records 
        tempTimeStamp.set(Calendar.YEAR, 1000);
        String tempCustomerEmail = "";
        int i = 0;
        List<PurchasedItem_Regular> duplicateTransactions = new ArrayList<PurchasedItem_Regular>();

        for (Object o : transactionDatas) {
            PurchasedItem_Regular p = (PurchasedItem_Regular) o;
            if (p.getCustomerEmail().equals(tempCustomerEmail) && p.getTime_stamp().equals(tempTimeStamp)) {
                duplicateTransactions.add(p);
            }
            tempTimeStamp = p.getTime_stamp();
            tempCustomerEmail = p.getCustomerEmail();
            i++;
        }
        for (Object o : duplicateTransactions) {
            PurchasedItem_Regular p = (PurchasedItem_Regular) o;
            transactionDatas.remove(p);
        }
        return transactionDatas;
    }

    //get the times of table bookings take place at an outlet from the data model's update time stamp
    private List<OutletReservation> getOutletReservations(String stakeholderName, Calendar fromTime) {
        Query q1 = em.createQuery("SELECT c FROM OutletReservation c WHERE c.outlet.stakeholder.name = :name AND c.customerShowUp=:customerShowUp AND "
                + "c.timeslot>:fromTime");
        q1.setParameter("name", stakeholderName);
        q1.setParameter("customerShowUp", true);
        q1.setParameter("fromTime", fromTime);

        return (List<OutletReservation>) q1.getResultList();
    }

    private Long[][] updateClusterW2Y(String stakeholderName, String[] countries, String[] ageGroups) {
        //initiallize cluster that updated data only within 2 years
        Long[][] cluster = new Long[countries.length][ageGroups.length];
        for (int i = 0; i < cluster.length; i++) {
            for (int j = 0; j < cluster[0].length; j++) {
                cluster[i][j] = new Long(0);
            }
        }
        //set fromTime to within 2 years
        Calendar fromTime = Calendar.getInstance();
        fromTime.set(fromTime.get(Calendar.YEAR) - 2, fromTime.get(Calendar.MONTH), 1, 0, 0, 0);
        //get reservation data within two years
        Query q1 = em.createQuery("SELECT c FROM OutletReservation c WHERE c.outlet.stakeholder.name = :name AND c.customerShowUp=:customerShowUp AND "
                + "c.timeslot>=:fromTime");
        q1.setParameter("name", stakeholderName);
        q1.setParameter("customerShowUp", true);
        q1.setParameter("fromTime", fromTime);
        List<OutletReservation> ors = q1.getResultList();
        //get purhcasedItme_Regular records with 2 years
        Query q2 = em.createQuery("SELECT c FROM PurchasedItem_Regular c WHERE c.seller = :name AND "
                + "c.time_stamp>=:fromTime ORDER BY c.time_stamp");
        q2.setParameter("name", stakeholderName);
        q2.setParameter("fromTime", fromTime);
        List<PurchasedItem_Regular> prs = getTransactionData(q2.getResultList());

        for (Object o : prs) {
            PurchasedItem_Regular p = (PurchasedItem_Regular) o;
            //get the country and agrGroup of this PurchasedItem
            String country = p.getBuyer_country();
            Integer age = p.getBuyer_age();
            String ageGroup = getAgeGroup(ageGroups, age);

            //Add one to the corrsponding 2dArray(cluster)  
            int x = getIndex(countries, country);
            int y = getIndex(ageGroups, ageGroup);
            cluster[x][y]++;
        }

        for (Object o : ors) {
            // get customer's country and ageGroup
            OutletReservation r = (OutletReservation) o;
            Users customer = r.getCustomer();
            String customerEmail = customer.getEmail();
            String country = customer.getCountry();
            Calendar birthDay = Calendar.getInstance();
            birthDay.setTime(customer.getBirthDate());
            Calendar currentTime = Calendar.getInstance();
            Integer age = new Integer(currentTime.get(Calendar.YEAR) - birthDay.get(Calendar.YEAR));
            String ageGroup = getAgeGroup(ageGroups, age);
            //add one to the corrsponding 2dArray(cluster)
            int x = getIndex(countries, country);
            int y = getIndex(ageGroups, ageGroup);
            cluster[x][y]++;
        }
        return cluster;
    }

    private int getIndex(String[] array, String word) {
        for (int i = 0; i < array.length; i++) {
            if (array[i].toUpperCase().trim().equals(word.toUpperCase().trim())) {
                return i;
            }
        }
        return 0;
    }

    private String getAgeGroup(String[] ageGroups, Integer age) {
        Integer minAge;
        Integer maxAge;
        for (int i = 0; i < ageGroups.length; i++) {
            String[] ageBoundaries = ageGroups[i].split("-");
            minAge = Integer.valueOf(ageBoundaries[0].trim());
            maxAge = Integer.valueOf(ageBoundaries[1].trim());
            if (minAge <= age && age < maxAge) {
                return ageGroups[i];
            }
        }
        return "ProblemOccurInGettingAgeGroup";
    }

    private List<StrDblRecord> updateSaleAgeGroupWithin(List<StrDblRecord> allRecords, String stakeholderName, int withinMonths, String[] ageGroups) {
        Calendar startTime = Calendar.getInstance();
        startTime.add(Calendar.MONTH, -withinMonths);
        startTime.set(startTime.get(Calendar.YEAR), startTime.get(Calendar.MONTH), 1, 0, 0, 0);
        Query q1 = em.createQuery("SELECT c FROM PurchasedItem_Regular c WHERE c.seller = :name AND "
                + "c.time_stamp>=:startTime");
        q1.setParameter("name", stakeholderName);
        q1.setParameter("startTime", startTime);
        List<PurchasedItem_Regular> pirs = q1.getResultList();
        List<StrDblRecord> strDblRecords = new ArrayList<StrDblRecord>();
        String type = "S_"+stakeholderName+"SaleAgeGroupW" + withinMonths + "M";
        for (int i = 0; i < pirs.size(); i++) {
            String ageGroup = getAgeGroup(ageGroups, pirs.get(i).getBuyer_age());
            Boolean existed = false;
            for (int j = 0; j < strDblRecords.size(); j++) {
                if ((strDblRecords.get(j).getType().equals(type)) && ageGroup.equals(strDblRecords.get(j).getAttribute())) {
                    strDblRecords.get(j).setNumDouble(strDblRecords.get(j).getNumDouble() + pirs.get(i).getSubtotal());
                    existed = true;
                }
            }
            if (!existed) {
                strDblRecords.add(new StrDblRecord(ageGroup, pirs.get(i).getSubtotal(), type));
            }
        }
        int a = 0;
        //update allRecords, delete old records, add new updated records from strDblRecords
        List<StrDblRecord> toBeRemovedRs = new ArrayList<StrDblRecord>();
        for (StrDblRecord sdr : allRecords) {
            if (sdr.getType().equals(type)) {
                toBeRemovedRs.add(sdr);
            }
        }
        for (StrDblRecord sdr : toBeRemovedRs) {
            Boolean removeSuc = allRecords.remove(sdr);
            System.out.println("This is the " + a + "th step, Currently removing record" + sdr.getAttribute() + "  " + sdr.getType());
            a++;
        }
        for (StrDblRecord sdr : strDblRecords) {
            allRecords.add(sdr);
        }
        int b = 1;
        for (StrDblRecord sdr : allRecords) {
            System.out.println("This is the " + b + "th record in the return clause is " + sdr.getAttribute() + "  " + sdr.getType());
            b++;
        }
        return allRecords;
    }

//delete the old StrDblRecord that replaced by new records through updateSaleAgeGroupWM
    private void deleteOldDblRecords(List<StrDblRecord> newRecords, String type) {
        Query q = em.createQuery("Select c from StrDblRecord c ");
        List<StrDblRecord> allRecords = q.getResultList();

        List<StrDblRecord> toBeRemovedRs = new ArrayList<StrDblRecord>();
        for (StrDblRecord sdr : allRecords) {
            if (sdr.getType().equals(type)) {
                toBeRemovedRs.add(sdr);
            }
        }
        for (StrDblRecord sdr : newRecords) {
            toBeRemovedRs.remove(sdr);
        }
        for (StrDblRecord sdr : toBeRemovedRs) {
            em.remove(sdr);
        }
        em.flush();
    }

//delete the old StrDblRecord that replaced by new records through updateSaleAgeGroupWM
    private void deleteOldLongRecords(List<StrLongRecord> newRecords, String type) {
        Query q = em.createQuery("Select c from StrLongRecord c ");
        List<StrLongRecord> allRecords = q.getResultList();
        List<StrLongRecord> toBeRemovedRs = new ArrayList<StrLongRecord>();
        for (StrLongRecord sdr : allRecords) {
            if (sdr.getType().equals(type)) {
                toBeRemovedRs.add(sdr);
            }
        }
        for (StrLongRecord sdr : newRecords) {
            toBeRemovedRs.remove(sdr);
        }
        for (StrLongRecord sdr : toBeRemovedRs) {
            em.remove(sdr);
        }
        em.flush();
    }

    private List<StrDblRecord> updateSaleCountryWithin(List<StrDblRecord> allRecords, String stakeholderName, int withinMonths) {
        Calendar startTime = Calendar.getInstance();
        startTime.add(Calendar.MONTH, -withinMonths);
        startTime.set(startTime.get(Calendar.YEAR), startTime.get(Calendar.MONTH), 1, 0, 0, 0);
        Query q1 = em.createQuery("SELECT c FROM PurchasedItem_Regular c WHERE c.seller = :name AND "
                + "c.time_stamp>=:startTime");
        q1.setParameter("name", stakeholderName);
        q1.setParameter("startTime", startTime);
        List<PurchasedItem_Regular> pirs = q1.getResultList();
        List<StrDblRecord> strDblRecords = new ArrayList<StrDblRecord>();
        String type = "S_"+stakeholderName+"SaleCountryW" + withinMonths + "M";
        for (int i = 0; i < pirs.size(); i++) {
            Boolean existed = false;
            for (int j = 0; j < strDblRecords.size(); j++) {
                if ((strDblRecords.get(j).getType().equals(type)) && pirs.get(i).getBuyer_country().trim().equals(strDblRecords.get(j).getAttribute())) {
                    strDblRecords.get(j).setNumDouble(strDblRecords.get(j).getNumDouble() + pirs.get(i).getSubtotal());
                    existed = true;
                }
            }
            if (!existed) {
                strDblRecords.add(new StrDblRecord(pirs.get(i).getBuyer_country().trim(), pirs.get(i).getSubtotal(), type));
            }
        }
        int a = 0;
        //update allRecords, delete old records, add new updated records from strDblRecords
        List<StrDblRecord> toBeRemovedRs = new ArrayList<StrDblRecord>();
        for (StrDblRecord sdr : allRecords) {
            if (sdr.getType().equals(type)) {
                toBeRemovedRs.add(sdr);
            }
        }
        for (StrDblRecord sdr : toBeRemovedRs) {
            Boolean removeSuc = allRecords.remove(sdr);
            System.out.println("This is the " + a + "th step, Currently removing record" + sdr.getAttribute() + "  " + sdr.getType());
            a++;
        }
        for (StrDblRecord sdr : strDblRecords) {
            allRecords.add(sdr);
        }
        int b = 1;
        for (StrDblRecord sdr : allRecords) {
            System.out.println("This is the " + b + "th record in the return clause is " + sdr.getAttribute() + "  " + sdr.getType());
            b++;
        }
        return allRecords;
    }

    private List<StrLongRecord> updateReservationAgeGroupWithin(List<StrLongRecord> allRecords, String stakeholderName, int withinMonths, String[] ageGroups) {
        Calendar startTime = Calendar.getInstance();
        startTime.add(Calendar.MONTH, -withinMonths);
        startTime.set(startTime.get(Calendar.YEAR), startTime.get(Calendar.MONTH), 1, 0, 0, 0);
        Query q1 = em.createQuery("SELECT c FROM OutletReservation c WHERE c.outlet.stakeholder.name = :name AND "
                + "c.customerShowUp=:customerShowUp AND c.timeslot>=:startTime ");
        q1.setParameter("name", stakeholderName);
        q1.setParameter("startTime", startTime);
        q1.setParameter("customerShowUp", true);
        List<OutletReservation> ors = q1.getResultList();
        String type = "S_"+stakeholderName+"ReservationAgeGroupW" + withinMonths + "M";
        List<StrLongRecord> strLongRecords = new ArrayList<StrLongRecord>();
        for (int i = 0; i < ors.size(); i++) {
            Boolean existed = false;
            Users customer = ors.get(i).getCustomer();
            Long plus = new Long(1);
            Calendar birthDay = Calendar.getInstance();
            birthDay.setTime(customer.getBirthDate());
            Calendar currentTime = Calendar.getInstance();
            Integer age = new Integer(currentTime.get(Calendar.YEAR) - birthDay.get(Calendar.YEAR));
            String ageGroup = getAgeGroup(ageGroups, age);
            for (int j = 0; j < strLongRecords.size(); j++) {
                if ((strLongRecords.get(j).getType().equals(type)) && ageGroup.equals(strLongRecords.get(j).getAttribute())) {
                    strLongRecords.get(j).setNumberLong(strLongRecords.get(j).getNumberLong() + plus);
                    existed = true;
                }
            }
            if (!existed) {
                strLongRecords.add(new StrLongRecord(ageGroup, plus, type));
            }
        }
        int a = 0;
        //update allRecords, delete old records, add new updated records from strDblRecords
        List<StrLongRecord> toBeRemovedRs = new ArrayList<StrLongRecord>();
        for (StrLongRecord sdr : allRecords) {
            if (sdr.getType().equals(type)) {
                toBeRemovedRs.add(sdr);
            }
        }
        for (StrLongRecord sdr : toBeRemovedRs) {
            Boolean removeSuc = allRecords.remove(sdr);
            System.out.println("This is the " + a + "th step, Currently removing record" + sdr.getAttribute() + "  " + sdr.getType());
            a++;
        }
        for (StrLongRecord sdr : strLongRecords) {
            allRecords.add(sdr);
        }
        int b = 1;
        for (StrLongRecord sdr : allRecords) {
            System.out.println("This is the " + b + "th record in the return clause is " + sdr.getAttribute() + "  " + sdr.getType());
            b++;
        }
        return allRecords;
    }

    private List<StrLongRecord> updateReservationCountryWithin(List<StrLongRecord> allRecords, String stakeholderName, int withinMonths) {
        Calendar startTime = Calendar.getInstance();
        startTime.add(Calendar.MONTH, -withinMonths);
        startTime.set(startTime.get(Calendar.YEAR), startTime.get(Calendar.MONTH), 1, 0, 0, 0);
        Query q1 = em.createQuery("SELECT c FROM OutletReservation c WHERE c.outlet.stakeholder.name = :name AND "
                + "c.customerShowUp=:customerShowUp AND c.timeslot>=:startTime ");
        q1.setParameter("name", stakeholderName);
        q1.setParameter("startTime", startTime);
        q1.setParameter("customerShowUp", true);
        List<OutletReservation> ors = q1.getResultList();
        String type = "S_"+stakeholderName+"ReservationAgeGroupW" + withinMonths + "M";
        List<StrLongRecord> strLongRecords = new ArrayList<StrLongRecord>();
        for (int i = 0; i < ors.size(); i++) {
            Boolean existed = false;
            Users customer = ors.get(i).getCustomer();
            Long plus = new Long(1);
            String country = customer.getCountry().trim();
            for (int j = 0; j < strLongRecords.size(); j++) {
                if ((strLongRecords.get(j).getType().equals(type)) && country.equals(strLongRecords.get(j).getAttribute())) {
                    strLongRecords.get(j).setNumberLong(strLongRecords.get(j).getNumberLong() + plus);
                    existed = true;
                }
            }
            if (!existed) {
                strLongRecords.add(new StrLongRecord(country, plus, type));
            }
        }
        int a = 0;
        //update allRecords, delete old records, add new updated records from strDblRecords
        List<StrLongRecord> toBeRemovedRs = new ArrayList<StrLongRecord>();
        for (StrLongRecord sdr : allRecords) {
            if (sdr.getType().equals(type)) {
                toBeRemovedRs.add(sdr);
            }
        }
        for (StrLongRecord sdr : toBeRemovedRs) {
            Boolean removeSuc = allRecords.remove(sdr);
            System.out.println("This is the " + a + "th step, Currently removing record" + sdr.getAttribute() + "  " + sdr.getType());
            a++;
        }
        for (StrLongRecord sdr : strLongRecords) {
            allRecords.add(sdr);
        }
        int b = 1;
        for (StrLongRecord sdr : allRecords) {
            System.out.println("This is the " + b + "th record in the return clause is " + sdr.getAttribute() + "  " + sdr.getType());
            b++;
        }
        return allRecords;
    }

    private List<StrDblRecord> updateMonthSale(List<StrDblRecord> strDblRecords, String stakeholderName, Calendar fromTime) {
        Calendar untilTime = Calendar.getInstance();
        Calendar startTime = Calendar.getInstance();
        untilTime.set(untilTime.get(Calendar.YEAR), untilTime.get(Calendar.MONTH), 1, 0, 0, 0);
//       untilTime.set(untilTime.get(Calendar.YEAR), 2, 1, 0, 0, 0);
        startTime.set(fromTime.get(Calendar.YEAR), fromTime.get(Calendar.MONTH), 1, 0, 0, 0);
        int numIntervalMonth = (untilTime.get(Calendar.YEAR) - startTime.get(Calendar.YEAR)) * 12
                + untilTime.get(Calendar.MONTH) - startTime.get(Calendar.MONTH);
        List<StrDblRecord> updatedRecords = new ArrayList<StrDblRecord>();
        Calendar startOfMonth = (Calendar) startTime.clone();
        Calendar endOfMonth = (Calendar) startTime.clone();
        for (int i = 0; i < numIntervalMonth; i++) {
            double sale = 0;
            endOfMonth.add(Calendar.MONTH, 1);
            Query query = em.createQuery("SELECT p FROM PurchasedItem_Regular p WHERE p.seller = :name"
                    + " AND p.time_stamp >= :startOfMonth AND p.time_stamp < :endOfMonth");
            query.setParameter("name", stakeholderName);
            query.setParameter("startOfMonth", startOfMonth);
            query.setParameter("endOfMonth", endOfMonth);

            //go through all purchasedItems of the month between startOfMonth and endOfMonth
            List<PurchasedItem_Regular> allItems = query.getResultList();
            for (PurchasedItem_Regular p : allItems) {
                sale += p.getSubtotal();
//                System.out.println("purchasedItemId" + p.getId() + "; subtotal:" + p.getSubtotal());
            }
            String attribute = endOfMonth.get(Calendar.YEAR) + "-" + endOfMonth.get(Calendar.MONTH);
//            System.out.println("sales from " + startOfMonth.get(Calendar.YEAR) + "-" + startOfMonth.get(Calendar.MONTH));
//            System.out.println("sales until " + attribute + ": " + sale);
            StrDblRecord newSalesRecord = new StrDblRecord(attribute, sale, "S_"+stakeholderName+"MonthSale");
            updatedRecords.add(newSalesRecord);
            startOfMonth.add(Calendar.MONTH, 1);
        }

        for (StrDblRecord sdrUpdated : updatedRecords) {
            Boolean hasRecorded = false;
            for (StrDblRecord sdr : strDblRecords) {
                if (sdr.getAttribute().trim().equals(sdrUpdated.getAttribute().trim())) {
                    sdr.setNumDouble(sdrUpdated.getNumDouble());
                    hasRecorded = true;
                }
            }
            if (!hasRecorded) {
                strDblRecords.add(sdrUpdated);
            }
        }

        return strDblRecords;
    }

    private List<StrLongRecord> updateMonthReservation(List<StrLongRecord> strLongRecords, String stakeholderName, Calendar fromTime) {
        Calendar untilTime = Calendar.getInstance();
        Calendar startTime = Calendar.getInstance();
        untilTime.set(untilTime.get(Calendar.YEAR), untilTime.get(Calendar.MONTH), 1, 0, 0, 0);
//       untilTime.set(untilTime.get(Calendar.YEAR), 2, 1, 0, 0, 0);
        startTime.set(fromTime.get(Calendar.YEAR), fromTime.get(Calendar.MONTH), 1, 0, 0, 0);
        int numIntervalMonth = (untilTime.get(Calendar.YEAR) - startTime.get(Calendar.YEAR)) * 12
                + untilTime.get(Calendar.MONTH) - startTime.get(Calendar.MONTH);
        List<StrLongRecord> updatedRecords = new ArrayList<StrLongRecord>();
        Calendar startOfMonth = (Calendar) startTime.clone();
        Calendar endOfMonth = (Calendar) startTime.clone();
        for (int i = 0; i < numIntervalMonth; i++) {
            Long numOfReservation = new Long(0);
            endOfMonth.add(Calendar.MONTH, 1);
            Query query = em.createQuery("SELECT c FROM OutletReservation c WHERE c.outlet.stakeholder.name = :name"
                    + " AND c.customerShowUp=:customerShowUp AND c.timeslot >= :startOfMonth AND c.timeslot < :endOfMonth");
            query.setParameter("name", stakeholderName);
            query.setParameter("startOfMonth", startOfMonth);
            query.setParameter("endOfMonth", endOfMonth);
            query.setParameter("customerShowUp", true);

            List<OutletReservation> ors = query.getResultList();
            for (OutletReservation or : ors) {
                numOfReservation++;
//                System.out.println("num of reservation is now" + numOfReservation);
            }
            String attribute = endOfMonth.get(Calendar.YEAR) + "-" + endOfMonth.get(Calendar.MONTH);
//            System.out.println("num of reservation from " + startOfMonth.get(Calendar.YEAR) + "-" + startOfMonth.get(Calendar.MONTH));
//            System.out.println("num of reservation until " + attribute + ": " + numOfReservation);
            StrLongRecord newReservationRecord = new StrLongRecord(attribute, numOfReservation, "S_"+stakeholderName+"MonthReservation");
            updatedRecords.add(newReservationRecord);
            startOfMonth.add(Calendar.MONTH, 1);
        }

        for (StrLongRecord slrUpdated : updatedRecords) {
            Boolean hasRecorded = false;
            for (StrLongRecord slr : strLongRecords) {
                if (slr.getAttribute().trim().equals(slrUpdated.getAttribute().trim())) {
                    slr.setNumberLong(slrUpdated.getNumberLong());
                    hasRecorded = true;
                }
            }
            if (!hasRecorded) {
                strLongRecords.add(slrUpdated);
            }
        }

        return null;
    }

    @Override
    public String printCluster(String stakeholderName) {

//        System.out.println("the length of dataModels is" + dataModels.size());
        Query q2 = em.createQuery("SELECT c FROM StakeholderDataModel c WHERE c.stakeholderName = :name");
        q2.setParameter("name", stakeholderName);
        List<StakeholderDataModel> stakeholderModels = q2.getResultList();
        if (stakeholderModels.isEmpty()) {
            return "/nThe outlet data model is empty";
        } else {
            StakeholderDataModel sdm = stakeholderModels.get(0);
            String clusterS = "";
            String[] countries = sdm.getCountriesArray();
            String[] ageGroups = sdm.getAgeGroupsArray();
            Long[][] cluster = sdm.getCluster();
            for (int i = 0; i < cluster.length; i++) {
                for (int j = 0; j < cluster[i].length; j++) {
                    clusterS = clusterS + countries[i] + "  " + ageGroups[j] + "  :" + "  " + cluster[i][j] + "   ";
                }
                clusterS = clusterS + "\n";
            }
            return clusterS;
        }
    }

    public String printClusterW2Y(String stakeholderName) {

//        System.out.println("the length of dataModels is" + dataModels.size());
        Query q2 = em.createQuery("SELECT c FROM StakeholderDataModel c WHERE c.stakeholderName = :name");
        q2.setParameter("name", stakeholderName);
        List<StakeholderDataModel> stakeholderModels = q2.getResultList();
        if (stakeholderModels.isEmpty()) {
            return "/nThe outlet data model is empty";
        } else {
            StakeholderDataModel sdm = stakeholderModels.get(0);
            String clusterS = "";
            String[] countries = sdm.getCountriesArray();
            String[] ageGroups = sdm.getAgeGroupsArray();
            Long[][] cluster = sdm.getClusterW2Y();
            for (int i = 0; i < cluster.length; i++) {
                for (int j = 0; j < cluster[i].length; j++) {
                    clusterS = clusterS + countries[i] + "  " + ageGroups[j] + "  :" + "  " + cluster[i][j] + "   ";
                }
                clusterS = clusterS + "\n";
            }
            return clusterS;
        }
    }

    @Override
    public String printSaleAgeGroups(Long outletId) {
        Query q1 = em.createQuery("SELECT c FROM OutletDataModel c WHERE c.outletId = :outletId");
        q1.setParameter("outletId", outletId);
        List<OutletDataModel> dataModels = q1.getResultList();

        if (dataModels.isEmpty()) {
            return "/nThe outlet data model is empty";
        } else {
            String saleAgeGroup = "";
            OutletDataModel odm = dataModels.get(0);
            List<StrDblRecord> sdrs = odm.getStrDblRecords();
            for (int i = 0; i < sdrs.size(); i++) {
                if (sdrs.get(i).getType().equals("S_SaleAgeGroup")) {
                    saleAgeGroup = saleAgeGroup + sdrs.get(i).getAttribute() + ":    " + sdrs.get(i).getNumDouble().toString() + "\n";
                }
            }
            return saleAgeGroup;
        }
    }

    public List<Helper_Record_TestingOnly> printOutletSaleRecords(Long outletId) {
        Outlet outlet = em.find(Outlet.class, outletId);
        if (outlet == null) {
            return null;
        }
        OutletDataModel dataModel = outlet.getOutletDataModel();
        List<Helper_Record_TestingOnly> returnList = new ArrayList<Helper_Record_TestingOnly>();
        for (StrDblRecord r : dataModel.getStrDblRecords()) {
            if (r.getType().equalsIgnoreCase("S_monthsale")) {
                returnList.add(new Helper_Record_TestingOnly(r.getAttribute(),
                        String.valueOf(r.getNumDouble()), r.getType()));
            }
        }
        return returnList;
    }

    @Override
    public List<Helper_Record_TestingOnly> printOutletReservationRecords(Long outletId) {
        Outlet outlet = em.find(Outlet.class, outletId);
        if (outlet == null) {
            return null;
        }
        OutletDataModel dataModel = outlet.getOutletDataModel();
        List<Helper_Record_TestingOnly> returnList = new ArrayList<Helper_Record_TestingOnly>();
        for (StrLongRecord r : dataModel.getStrLongRecords()) {
            if (r.getType().equalsIgnoreCase("monthreservation")) {
                returnList.add(new Helper_Record_TestingOnly(r.getAttribute(),
                        String.valueOf(r.getNumberLong()), r.getType()));
            }
        }
        return returnList;
    }

    @Override
    public void initializeRecord(Long outletId) {
        Outlet outlet = em.find(Outlet.class, outletId);
        OutletDataModel dataModel = outlet.getOutletDataModel();
        List<StrLongRecord> allStrLongs = dataModel.getStrLongRecords();
        List<StrDblRecord> allStrDbls = dataModel.getStrDblRecords();
        for (StrLongRecord r1 : allStrLongs) {
            em.remove(r1);
        }
        for (StrDblRecord r2 : allStrDbls) {
            em.remove(r2);
        }
        dataModel.setStrDblRecords(new ArrayList<StrDblRecord>());
        dataModel.setStrLongRecords(new ArrayList<StrLongRecord>());
        em.persist(dataModel);
    }
}
